//
//  ZJDynamic.swift
//  Pods-ZJSwiftKit_Example
//
//  Created by 万孟超 on 2023/10/9.
//

import Foundation

/**
 * 通过ZJDynamic包装实现动态成员
 * 动态添加成员属性在Xcode中不会有代码补全提示
 * 只有Class才能添加动态成员属性
 * 因为结构体是值类型
 * 重新获取zjDy时动态设置的成员就没有了
 *
 *      class ZJDynamicClass: ZJDynamicProtocol {
 *          var a: String = "123"
 *          var b: Int = 0
 *      }
 *      let dataModel = ZJDynamicClass()
 *
 *      dataModel.zjDy.a = "abc"
 *      print(dataModel.zjDy.a)                       // optput "abc"
 *
 *      dataModel.zjDy.b = 100
 *      print(dataModel.zjDy.b)                       // optput 100
 *
 *      dataModel.zjDy.dynamicProperty = "this is dynamicProperty"
 *      print(dataModel.zjDy.dynamicProperty as Any)  // optput "this is dynamicProperty"
 *
 */

@dynamicMemberLookup
public class ZJDynamic<ZJDynamicObject> {
    public private(set) var object: ZJDynamicObject
    
    /// 额外的属性字典
    public private(set) var dynamicProperty: [String : Any]
    
    /// 初始化包装器
    /// - Parameter object: 包装的实例
    public init(_ object: ZJDynamicObject, dynamicProperty: [String : Any] = [:]) {
        self.object = object
        self.dynamicProperty = dynamicProperty
    }
    
    /// 包装的实例的属性set和get
    public subscript<Value>(dynamicMember keyPath: WritableKeyPath<ZJDynamicObject, Value>) -> Value {
        get { object[keyPath: keyPath] }
        set { object[keyPath: keyPath] = newValue }
    }
    
    /// 额外的属性set和get
    public subscript(dynamicMember member: String) -> Any? {
        get { dynamicProperty[member] }
        set { dynamicProperty[member] = newValue }
    }
}


/// 动态成员属性协议，只能被Class遵循，详解见ZJDynamic
public protocol ZJDynamicProtocol: AnyObject { }

fileprivate var objectDynamicKey: UInt8 = 0

extension ZJDynamicProtocol {
    
    private var staticObj: ZJDynamic<Self>? {
        get { objc_getAssociatedObject(self, &objectDynamicKey) as? ZJDynamic<Self> }
        set { objc_setAssociatedObject(self, &objectDynamicKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
    
    private var obj: ZJDynamic<Self> {
        get {
            if let staticObj = staticObj {
                return staticObj
            }else{
                let newObj = ZJDynamic(self)
                staticObj = newObj
                return newObj
            }
        }
    }
    
    /// ZJPrefix extensions.
    public static var zjDy: ZJDynamic<Self>.Type {
        get{ ZJDynamic.self }
        set{ }
    }
    
    /// ZJPrefix extensions.
    public var zjDy: ZJDynamic<Self> {
        get { obj }
        set { }
    }
}

extension NSObject: ZJDynamicProtocol { }
